﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Microsoft.WindowsMobile.Status;
using Microsoft.Win32;
using System.Globalization;
using System.Reflection;

namespace Senthil.Mobile.WhereAmI
{
    public partial class MainForm : Form
    {
        const string unknown = "<Unknown>";

        RouteTracker routeTracker;
        ILocationChangeNotifier locationChangeNotifier;
        NotificationManager notificationManager;
        SMSHandler smsHandler = new SMSHandler();

        LocationAndETAWatcher watcher;

        SoundPlayForm soundPlayForm;
        
        public MainForm()
        {
            InitializeComponent();
            notificationManager = new NotificationManager(SystemState.OwnerName, smsHandler, smsHandler);
            notificationManager.Disable();
            watcher = new LocationAndETAWatcher();

            HookupSoundPlayFormToSoundPlayer();
            
            //if (!LocationChangeNotifier.IsDeviceLocationChangeNotificationEnabled())
            //{
            //    DialogResult result = MessageBox.Show(
            //        "Cell broadcast doesn't seem to be enabled in your phone. Do you still want to continue?",
            //        Program.ApplicationName,
            //        MessageBoxButtons.YesNo,
            //        MessageBoxIcon.Question,
            //        MessageBoxDefaultButton.Button1);

            //    if (result != DialogResult.Yes)
            //    {
            //        Application.Exit();
            //        return;
            //    }
            //}
        }

        private void HookupSoundPlayFormToSoundPlayer()
        {
            CustomSoundPlayer.PlayingSound += new EventHandler<EventArgs>((sender, args) =>
            {
                this.BeginInvoke(
                    new Action(() =>
                    {
                        using (soundPlayForm = new SoundPlayForm())
                        {
                            soundPlayForm.ShowDialog();
                        }
                    }));
            });

            CustomSoundPlayer.StoppedPlaying += new EventHandler<EventArgs>((sender, args) =>
            {
                if (soundPlayForm != null && !soundPlayForm.IsDisposed)
                    soundPlayForm.Close();
            });
        }

        private void selectRouteMenuItem_Click(object sender, EventArgs e)
        {
            SelectRoute();
        }

        private void SelectRoute()
        {
            using (RouteSelectorForm form = new RouteSelectorForm())
            {
                form.Owner = this;
                if (form.ShowDialog() != DialogResult.Cancel)
                {
                    ShowETA(destinationETALabel, null, false);
                    lastKnownLocationLabel.Text = locationGroupLabel.Text =
                        destinationTimeSpanLabel.Text = locationGroupETALabel.Text = string.Empty;

                    ClearLocationGroupsUI();
                    
                    string routeName = form.SelectedRoute;
                    ThreadPool.QueueUserWorkItem(AsyncLoadRoute, routeName);
                    ShowLoading();
                }
            }
        }

        private void ClearLocationGroupsUI()
        {
            locationGroupsComboBox.Items.Clear();
            locationGroupsComboBox.Visible = false;
        }

        private void ShowLoading()
        {
            loadingLabel.Visible = true;
            loadingLabel.BringToFront();

            topPanel.Visible = bottomPanel.Visible = false;
        }

        private void HideLoading()
        {
            topPanel.Visible = bottomPanel.Visible = true;
            bottomPanel.BringToFront();
            loadingLabel.SendToBack();
            loadingLabel.Visible = false;
            locationGroupsComboBox.Focus();
        }

        private void AsyncLoadRoute(object state)
        {
            try
            {
                Route route = RouteManager.LoadRouteByName((string)state);
                this.Invoke(new Action<Route>(NewRouteSelected), route);
            }
            catch (Exception e)
            {
                CrashHandler.HandleCrash(Program.ShortApplicationName, e);
            }
        }

        private void NewRouteSelected(Route route)
        {
            routeNameLabel.Text = "ETA for " + route.Name;
            destinationLabel.Text = route.DestinationGroup.Name;

            if (routeTracker != null)
            {
                routeTracker.LocationChanged -= new EventHandler<LocationChangedEventArgs>(routeTracker_LocationChanged);
                routeTracker.TimeChanged -= new EventHandler<TimeChangedEventArgs>(routeTracker_ETAChanged);
            }

            if (locationChangeNotifier == null)
                locationChangeNotifier = new LocationChangeNotifier();

            routeTracker = new RouteTracker(route, locationChangeNotifier);
            routeTracker.LocationChanged += new EventHandler<LocationChangedEventArgs>(routeTracker_LocationChanged);
            routeTracker.TimeChanged += new EventHandler<TimeChangedEventArgs>(routeTracker_ETAChanged);

            Settings s = Settings.Load();
            HideLoading();
            
            ConfigureWatcherFromSettings(s);
            routeTracker.Initialize();
            
            ConfigureNotificationManager(s.AutoRespondToSMS, s.ConfiguredMessageText, s.ConfiguredUsers);
            notificationManager.RouteTracker = routeTracker;
        }
        
        void routeTracker_ETAChanged(object sender, TimeChangedEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action(() => routeTracker_ETAChanged(sender, e)));
                return;
            }

            TimeSpan timeToDestination = e.TimeToDestination;
            destinationTimeSpanLabel.Text = GetFormattedTimeSpan(timeToDestination);
            ShowETA(destinationETALabel, timeToDestination, false);

            if (locationGroupsComboBox.Items.Count == 0)
                return;

            if (locationGroupsComboBox.SelectedIndex == 0 && ((LocationGroup)locationGroupsComboBox.Items[0]).IsSpecial)
            {
                ShowETA(locationGroupETALabel, e.TimeToNextSignificantLocationGroup.Value, true);
            }
            else
            {
                LocationGroup group = locationGroupsComboBox.SelectedItem as LocationGroup;
                if (group != null && routeTracker.CanCalculateTimeSpan())
                {
                    TimeSpan timeSpan = routeTracker.GetTimeSpanFor(group);
                    ShowETA(locationGroupETALabel, timeSpan, true);
                }
            }
        }

        void routeTracker_LocationChanged(object sender, LocationChangedEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action(() => routeTracker_LocationChanged(sender, e)));
                return;
            }

            bool isLocationRecognized = e.Location.IsRecognized;
            bool isLocationGroupKnown = e.Location.LocationGroup != null;
            bool noLocationsKnown = !isLocationRecognized && e.LastKnownLocation == null;

            locationLabel.ForeColor = isLocationRecognized ? Color.Black : Color.Gray;
            lastKnownLocationLabel.Visible = !isLocationGroupKnown;
            locationGroupLabel.Visible = isLocationGroupKnown;

            locationLabel.Text = e.Location.Name;
            
            if (isLocationRecognized || e.Location.LocationGroup != null)
            {
                UpdateUIForKnownLocation(e);
            }
            else
            {
                if (!noLocationsKnown)
                {
                    lastKnownLocationLabel.Text = string.Format("{0} ({1})", e.LastKnownLocation.LocationGroup.Name, e.LastKnownLocation.Name);
                    lastKnownLocationLabel.BringToFront();
                }
            }
        }

        private void UpdateUIForKnownLocation(LocationChangedEventArgs e)
        {
            locationGroupLabel.BringToFront();
            locationGroupLabel.Text = e.Location.LocationGroup.Name;
            if (e.LastKnownLocation == null || e.Location.LocationGroup != e.LastKnownLocation.LocationGroup)
            {
                UpdateLocationGroupUI(e);
            }
        }

        private void UpdateLocationGroupUI(LocationChangedEventArgs e)
        {
            locationGroupsComboBox.Items.Clear();

            // If we are at destination location group or the one before it, hide the comobo box, as the destination information
            // is always displayed.
            if (!e.LocationGroupsAfter.Any() || e.LocationGroupsAfter.First() == routeTracker.DestinationLocationGroup)
            {
                locationGroupsComboBox.Visible = false;
                locationGroupETALabel.Text = "";
            }
            else
            {
                locationGroupsComboBox.Visible = true;

                foreach (LocationGroup locationGroup in e.LocationGroupsAfter)
                {
                    locationGroupsComboBox.Items.Add(locationGroup);
                }

                locationGroupsComboBox.SelectedItem = e.NextSignificantLocationGroup ?? locationGroupsComboBox.Items[0];
            }
        }

        private void closeMenuItem_Click(object sender, EventArgs e)
        {
            //RouteManager.Save();
            
            this.Close();
        }

        private void minuteTimer_Tick(object sender, EventArgs e)
        {
            if (routeTracker != null && routeTracker.CanCalculateTimeSpan())
                routeTracker.RecalculateTime();
        }

        private static string GetFormattedTimeSpan(TimeSpan timeSpan)
        {
            return
                (timeSpan.Days > 0 ? (timeSpan.Days.ToString(CultureInfo.CurrentCulture) + "d ") : "") +
                (timeSpan.Hours > 0 ? (timeSpan.Hours.ToString(CultureInfo.CurrentCulture) + "h ") : "") +
                timeSpan.Minutes.ToString(CultureInfo.CurrentCulture) + "m";
        }

        private void locationGroupsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            LocationGroup locationGroup = (LocationGroup)
                locationGroupsComboBox.Items[locationGroupsComboBox.SelectedIndex];

            if (routeTracker.CanCalculateTimeSpan())
            {
                TimeSpan timeSpan = routeTracker.GetTimeSpanFor(locationGroup);
                ShowETA(locationGroupETALabel, timeSpan, true);
            }
        }

        private static void ShowETA(Label label, TimeSpan? timeSpan, bool showTimeSpan)
        {
            if (timeSpan == null)
            {
                label.Text = unknown;
                label.ForeColor = Color.Red;
                return;
            }
            else
            {
                label.ForeColor = SystemColors.ControlText;
            }

            DateTime now = DateTime.Now;
            DateTime dt = now.Add(timeSpan.Value);

            string format = "HH:mm";
            if (dt.Day != now.Day)
            {
                format = "dd MMM " + format;
            }

            label.Text = DateTime.Now.Add(timeSpan.Value).ToString(format, CultureInfo.CurrentCulture) + (showTimeSpan ? " (" + GetFormattedTimeSpan(timeSpan.Value) + ")" : "");
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (!this.Visible)
                return;

            int screenWidth = this.Width;
            int screenHeight = this.Height;

            foreach (Control c in bottomPanel.Controls)
            {
                c.Width = screenWidth - 2;
            }

            if (screenHeight < screenWidth)
            {
                AdjustUIForLandscapeMode(screenWidth);
            }
            else
            {
                AdjustUIForPortraitMode(screenWidth);
            }
        }

        private void AdjustUIForPortraitMode(int screenWidth)
        {
            this.SuspendLayout();
            topPanel.Dock = DockStyle.None;

            //locationLabel.TextAlign = locationGroupLabel.TextAlign =
            //    lastKnownLocationLabel.TextAlign = ContentAlignment.TopLeft;

            locationGroupLabel.Top = lastKnownLocationLabel.Top = locationLabel.Bottom + 1;
            topPanel.Height = locationGroupLabel.Bottom + lineLabel.Height + 2;

            foreach (Control c in topPanel.Controls)
            {
                c.Width = screenWidth - 2;
                c.Left = 1;
            }

            lineLabel.Dock = DockStyle.None;
            lineLabel.Dock = DockStyle.Bottom;

            topPanel.Dock = DockStyle.Top;

            bottomPanel.Dock = DockStyle.None;
            locationGroupsComboBox.Width = locationGroupETALabel.Width = screenWidth - 2;
            locationGroupETALabel.Left = 1;
            locationGroupETALabel.Top = locationGroupsComboBox.Bottom + 2;
            bottomPanel.Dock = DockStyle.Fill;

            this.ResumeLayout();
        }

        private void AdjustUIForLandscapeMode(int screenWidth)
        {
            // Landscape mode
            //locationLabel.TextAlign = locationGroupLabel.TextAlign =
            //    lastKnownLocationLabel.TextAlign = ContentAlignment.TopCenter;

            // Split top panel area into two columns, first column for location and second for location group
            this.SuspendLayout();
            topPanel.Dock = DockStyle.None;
            locationLabel.Width = locationGroupLabel.Width = lastKnownLocationLabel.Width = (screenWidth / 2) - 1;
            locationGroupLabel.Left = lastKnownLocationLabel.Left = locationLabel.Width + 1;
            locationGroupLabel.Top = lastKnownLocationLabel.Top = locationLabel.Top;

            topPanel.Height = locationLabel.Bottom + lineLabel.Height + 2;

            lineLabel.Dock = DockStyle.None;
            lineLabel.Dock = DockStyle.Bottom;

            topPanel.Dock = DockStyle.Top;

            // Split bottom panel area into two columns, one for locationGroupCombo and other for showing est time

            bottomPanel.Dock = DockStyle.None;
            locationGroupsComboBox.Width = locationGroupETALabel.Width = screenWidth / 2;
            locationGroupETALabel.Left = locationGroupsComboBox.Right + 1;
            locationGroupETALabel.Top = locationGroupsComboBox.Top;
            bottomPanel.Dock = DockStyle.Fill;

            this.ResumeLayout();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            SelectRoute();
        }

        private void aboutMenuItem_Click(object sender, EventArgs e)
        {
            using (About about = new About())
            {
                about.ApplicationName = Program.ApplicationName;
                about.Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                about.ApplicationDescription = "Location based services based on cell broadcast and cell tower id.";
                about.Author = "Senthil Kumar Selvaraj";
                about.ContactInfo = "senthil.thecoder@gmail.com";

                about.ShowDialog();
            }
        }

        private void autoSMSMenuItem_Click(object sender, EventArgs e)
        {
            Settings s = Settings.Load();

            using (AutoSMSConfigureForm autoSMSConfigureForm = new AutoSMSConfigureForm(s.AutoRespondToSMS, s.ConfiguredMessageText, s.ConfiguredUsers))
            {
                if (autoSMSConfigureForm.ShowDialog() == DialogResult.OK)
                {
                    ConfigureNotificationManager(autoSMSConfigureForm.AutoSMSReplyEnabled,
                        autoSMSConfigureForm.ConfiguredMessage,
                        autoSMSConfigureForm.Users);

                    s.AutoRespondToSMS = autoSMSConfigureForm.AutoSMSReplyEnabled;
                    s.ConfiguredMessageText = autoSMSConfigureForm.ConfiguredMessage;
                    s.ConfiguredUsers = autoSMSConfigureForm.Users;

                    s.Save();
                }
            }
        }

        private void ConfigureNotificationManager(bool enable, string configuredMessageText, string[] users)
        {
            if (enable)
                notificationManager.Enable();
            else
                notificationManager.Disable();

            notificationManager.NotifyUserWhenSMSInput(users, configuredMessageText);
        }

        private void ConfigureWatcherFromSettings(Settings s)
        {
            if (s.PlaySoundsOnEvents)
            {
                watcher.Initialize(routeTracker);

                if (!string.IsNullOrEmpty(s.SoundPlayLocationGroupName))
                {
                    watcher.AddSingleShotActionForLocationChange(locationGroup => locationGroup.Name == s.SoundPlayLocationGroupName,
                        () => PlaySound(s.SoundFilePath));
                }

                var timeSpanToDestinationConfiguration = s.TimeSpanToDestination;
                if (timeSpanToDestinationConfiguration != TimeSpan.Zero)
                {
                    watcher.AddSingleShotActionForETA(liveTimeSpanToDestination => liveTimeSpanToDestination <= timeSpanToDestinationConfiguration,
                        () => PlaySound(s.SoundFilePath));
                }
            }
            else
            {
                watcher.Disable();
            }
        }

        private void PlaySound(string path)
        {
            try
            {
                CustomSoundPlayer.PlaySound(path);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString(), "Could not play sound");
            }
        }

        private void playSoundMenuItem_Click(object sender, EventArgs e)
        {
            if (routeTracker == null)
            {
                MessageBox.Show("Configuring Play Sound requires a valid route. Please select a route first.");
                return;
            }

            using (SoundConfigureForm form = new SoundConfigureForm())
            {
                Settings s = Settings.Load();

                form.SoundNotificationsEnabled = s.PlaySoundsOnEvents;
                form.SoundFileName = s.SoundFilePath;
                form.LocationGroupName = s.SoundPlayLocationGroupName;
                form.LocationGroups = routeTracker.LocationGroups;
                form.TimeSpanToDestination = s.TimeSpanToDestination;


                if (form.ShowDialog() == DialogResult.OK)
                {
                    s.PlaySoundsOnEvents = form.SoundNotificationsEnabled;
                    s.SoundFilePath = form.SoundFileName;
                    s.SoundPlayLocationGroupName = form.LocationGroupName;
                    s.TimeSpanToDestination = form.TimeSpanToDestination;
                    
                    s.Save();

                    ConfigureWatcherFromSettings(s);
                }
            }
        }
    }
}